#include <stdio.h>
#include <math.h>
#include <gmp.h>
#include <time.h>
#include <stdlib.h>

/* 10.000.000 de dígitos, em bits  33219281. */
#define bits_precisao 33219281

/* Variáveis globais para comparar o erro. */
mpf_t erro_atual, erro_maximo;

/* Funcao utilizacao para pegar o tempo atual do relogio. */
double tempo()
{
	struct timeval tv;
	gettimeofday(&tv,0);
	return tv.tv_sec + tv.tv_usec/1e6;
}

/* Funcao que calcula se a precisao desejada já foi atingida. */
int precisao(mpf_t pi_atual, mpf_t pi_aproximado){

	mpf_sub(erro_atual, pi_atual, pi_aproximado);
	mpf_abs(erro_atual, erro_atual);
	if(mpf_cmp(erro_atual, erro_maximo) < 0){		
		return 1;
	}
	return 0;
}


int main(void){
	
	FILE *output_pi_calculado, *input_pi_aproximado;

	/* Variáveis para registrar o tempo. */
	double tempo_inicio, tempo_fim;

	/* Variaveis usadas no método de Borwein utilizando a biblioteca GMP. */
	mpf_t a0, a1, b0, b1, p0, p1, pi, aux;
	mpf_t pi_aproximado;

	/* Numero da iteracao atual. */
	int iteracao = 0;

	/* Setando a precisão padrao para as variaveis a serem inicializadas. */
	mpf_set_default_prec( bits_precisao );

	/*Inicializa as variaveis. */
	mpf_init(a0);
	mpf_init(a1);
	mpf_init(b0);
	mpf_init(b1);
	mpf_init(p0);
	mpf_init(p1);
	mpf_init(aux);
	mpf_init(pi_aproximado);

	/* Inicializa as variáveis de erro. */
	mpf_init(erro_atual);
	mpf_init(erro_maximo);

	/* Carrega o valor de pi calculado, do arquivo. */
	input_pi_aproximado = fopen("pi_aproximado.txt", "r");	
	mpf_inp_str(pi_aproximado, input_pi_aproximado, 0);
	

	/* Valor do erro máximo, ou seja, número de dígitos corretos. */
	mpf_set_str(erro_maximo, "1e-10000000", 0);

	/* Registra o tempo inicial */
	tempo_inicio = tempo();

	/* Setando a0. */
	mpf_sqrt_ui(a0, 2.0);

	/* Setando b0. */
	mpf_set_ui(b0, 0.0);

	/* Setando p0. */
	mpf_set_ui(p0, 2.0);
	mpf_add(p0, p0, a0);


	/* Iterações de calculo do metodo de Borwein. */
	while(1){

		/* Calculando a1. */
		mpf_sqrt(aux, a0);
		mpf_ui_div(a1, 1.0, aux);
		mpf_add(a1, a1, aux);
		mpf_div_ui(a1, a1, 2.0);


		/* Calculando b1. */
		mpf_add_ui(b1, b0, 1.0);
		mpf_mul(b1, b1, aux);
		mpf_add(aux, a0, b0);
		mpf_div(b1, b1, aux);


		/* Calculando p1. */
		mpf_add_ui(p1, a1, 1.0);
		mpf_mul(p1, p1, p0);
		mpf_mul(p1, p1, b1);
		mpf_add_ui(aux, b1, 1.0);
		mpf_div(p1, p1, aux);

		iteracao++;

		/* Condição de parada do algoritmo (se alcançar a precisão desejada). */
		if(precisao(p1, pi_aproximado))
			break;


		/* Atualiza as variaveis para proxima iteração. */
		mpf_set(a0, a1);
		mpf_set(b0, b1);
		mpf_set(p0, p1);

	}

	/* Registra o tempo final. */
	tempo_fim = tempo();

	/* Abre o arquivo onde será salvo o resultado. */
	output_pi_calculado = fopen("borwein_sequencial.txt", "a+");

	printf("Borwein Sequencial executado com sucesso\n");
	
	/* Salva informacoes sobre a execucao em um arquivo. */
	fprintf(output_pi_calculado, "\n--------------------------------------------------------\n");
	fprintf(output_pi_calculado, " Borwein Sequencial executado com sucesso.\n");
	fprintf(output_pi_calculado, " Os primeiros 30 digitos do resultado sao : ");
	mpf_out_str(output_pi_calculado, 10, 30, p1);
	fprintf(output_pi_calculado, "\n O tempo gasto com a execucao foi de %lf segundos.\n", tempo_fim - tempo_inicio);
	fprintf(output_pi_calculado, " O numero de iteracoes necessarias foi %d.\n", iteracao);
	

	fclose(output_pi_calculado);
	fclose(input_pi_aproximado);
	
	return 0;
}
